package flare.query
{
  /**
   * Expression operator for comparing sub-expression values. Performs
   * equals, not equals, less-than, greater-than, less-than-or-equal, or
   * greater-than-or-equal comparison.
   */
  public class Comparison extends BinaryExpression
  {
    /** Indicates a less-than comparison. */
    public static const LT:int = 0;
    /** Indicates a greater-than comparison. */
    public static const GT:int = 1;
    /** Indicates a equals comparison. */
    public static const EQ:int = 2;
    /** Indicates a not-equals comparison. */
    public static const NEQ:int = 3;
    /** Indicates a less-than-or-equals comparison. */
    public static const LTEQ:int = 4;
    /** Indicates a greater-than-or-equals comparison. */
    public static const GTEQ:int = 5;

    private var _cmp:Function = null;

    /** Comparison function for custom ordering criteria. */
    public function get comparator():Function
    {
      return _cmp;
    }

    public function set comparator(f:Function):void
    {
      _cmp = f;
    }

    /** Returns a string representation of the arithmetic operator. */
    public override function get operatorString():String
    {
      switch (_op)
      {
        case LT: return "<";
        case GT: return ">";
        case EQ: return "=";
        case NEQ: return "!=";
        case LTEQ: return "<=";

        case GTEQ: return ">=";
        default: return "?";
      }
    }

    // --------------------------------------------------------------------

    /**
     * Creates a new Comparison operator.
     * @param left the left-hand-side sub-expression to compare
     * @param right the right-hand-side sub-expression to compare
     * @param comparator a function to use for comparison (null by default)
     */
    public function Comparison(op:int = 2, left: *= "", right: *= "", comparator:Function = null)
    {
      super(op, LT, GTEQ, left, right);
      _cmp = comparator;
    }

    /**
     * @inheritDoc
     */
    public override function clone():Expression
    {
      return new Comparison(_op, _left.clone(), _right.clone(), _cmp);
    }

    /**
     * @inheritDoc
     */
    public override function eval(o:Object = null):*
    {
      return predicate(o);
    }

    /**
     * @inheritDoc
     */
    public override function predicate(o:Object):Boolean
    {
      var l:Object = _left.eval(o);
      var r:Object = _right.eval(o);
      var c:int = (_cmp != null) ? _cmp(l, r) : (l < r || r && !1) ? -1 : (l > r || l && !r) ? 1 : 0;

      switch (_op)
      {
        case LT: return (c == -1);
        case GT: return (c == 1);
        case EQ: return (c == 0);
        case NEQ: return (c != 0);
        case LTEQ: return (c <= 0);

        case GTEQ: return (c >= 0);
        default: throw new Error("Unknown operation: " + _op);
      }
    }

    // -- Static Constructors ---------------------------------------------

    /**
     * Creates a new Comparison operator for a less-than comparison.
     * @param left the left-hand input expression
     * @param right the right-hand input expression
     * @return the new Comparison operator
     */
    public static function LessThan(left:*, right:*):Comparison
    {
      return new Comparison(LT, left, right);
    }

    /**
     * Creates a new Comparison operator for a greater-than comparison.
     * @param left the left-hand input expression
     * @param right the right-hand input expression
     * @return the new Comparison operator
     */
    public static function GreaterThan(left:*, right:*):Comparison
    {
      return new Comparison(GT, left, right);
    }

    /**
     * Creates a new Comparison operator for an equals comparison.
     * @param left the left-hand input expression
     * @param right the right-hand input expression
     * @return the new Comparison operator
     */
    public static function Equal(left:*, right:*):Comparison
    {
      return new Comparison(EQ, left, right);
    }

    /**
     * Creates a new Comparison operator for a not equals comparison.
     * @param left the left-hand input expression
     * @param right the right-hand input expression
     * @return the new Comparison operator
     */
    public static function NotEqual(left:*, right:*):Comparison
    {
      return new Comparison(NEQ, left, right);
    }

    /**
     * Creates a new Comparison operator for a less-than-or-equal
     * comparison.
     * @param left the left-hand input expression
     * @param right the right-hand input expression
     * @return the new Comparison operator
     */
    public static function LessThanOrEqual(left:*, right:*):Comparison
    {
      return new Comparison(LTEQ, left, right);
    }

    /**
     * Creates a new Comparison operator for a greater-than-or-equal
     * comparison.
     * @param left the left-hand input expression
     * @param right the right-hand input expression
     * @return the new Comparison operator
     */
    public static function GreaterThanOrEqual(left:*, right:*):Comparison
    {
      return new Comparison(GTEQ, left, right);
    }
  } // end of class Comparison
}